function SignupClassic(param) {
try{
	var that = this;

	this.j_signup_tip_bubble = null;
	this.j_signup_tip = null;
	this.j_signup_form = null;
	this.j_signup_email = null;
	this.j_signup_username = null;
	this.j_signup_password = null;
	this.j_signup_terms_of_service = null;
	this.j_signup_email_error_msg = null;
	this.j_signup_username_error_msg = null;
	this.j_signup_password_error_msg = null;
	this.j_signup_tos_error_bubble = null;
	this.j_signup_submit = null;
	this.j_signup_submit_status = null;
	
	var email_check_url = "";
	
	var email_correct = false;
	var username_correct = false;
	var password_correct = false;
	var tos_correct = false;

	var email_existing = false;
	
	var email_validation_locked = true;
	var email_existing_validation_locked = true;
	var username_validation_locked = true;
	var password_validation_locked = true;
	var tos_validation_locked = true;
	
	var email_validation_ever_opened = false;
	var username_validation_ever_opened = false;
	var password_validation_ever_opened = false;
	var tos_validation_ever_opened = false;
	
	var form_submiting = false;
	
	var email_existing_validating = false;
	var email_existing_validated = false;
	
	var jqxhr_email = null;
	var error_occured = false;
	
	function __construct(p) {
	try{
		for(pkey in p) {
			if(pkey[0] != "_") {
				that[pkey] = p[pkey];
			}
			if(p._email_check_url) {
				email_check_url = p._email_check_url;
			}
		}
		jq("[data-explaination-text]").focus(showSignupTip).blur(hideSignupTip);
		that.j_signup_submit.mousedown(signupSumbitMousedown);
		that.j_signup_form.submit(signupFormSubmit);
		that.j_signup_email.blur(emailInputBlur);
		that.j_signup_username.blur(usernameInputBlur);
		that.j_signup_password.blur(passwordInputBlur);
		that.j_signup_email.change(emailInputChange);
		that.j_signup_username.change(usernameInputChange);
		that.j_signup_password.change(passwordInputChange);
		that.j_signup_terms_of_service.change(tosCheckboxChange);
		
	}catch(err) {
		alert(err);
	}
	}
		
	function emailInputChange() {
	try{
		if(email_validation_locked) {
			email_validation_locked = false;
		}
		if(email_existing_validation_locked) {
			email_existing_validation_locked = false;
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function emailInputBlur() {
	try{
		if(!form_submiting) {
			validateEmail();
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function usernameInputChange() {
	try{
		if(username_validation_locked) {
			username_validation_locked = false;
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function usernameInputBlur() {
	try{
		if(!form_submiting) {
			validateUsername();
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function passwordInputChange() {
	try{
		if(password_validation_locked) {
			password_validation_locked = false;
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function passwordInputBlur() {
	try{
		if(!form_submiting) {
			validatePassword();
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function validateEmail() {
	try{
		if(!email_validation_locked) {
			email_validation_locked = true;
	    	if(!email_validation_ever_opened) {
	    		email_validation_ever_opened = true;
	    	}
			var email = that.j_signup_email.val();
			var emailLength = jq.trim(email).length;
	
			// check required
			if(!emailLength) {
				email_correct = false;
				that.j_signup_email.data({ 'errorMsg' : "Must not be empty" });
				showWarningMsg(that.j_signup_email, that.j_signup_email_error_msg);
				return false;
			}
			// check regex
			if(email.search(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/) == -1) {
				email_correct = false;
				that.j_signup_email.data({ 'errorMsg' : "Not a valid email" });
				showWarningMsg(that.j_signup_email, that.j_signup_email_error_msg);
				return false;
			}
			email_correct = true;
			removeWarningMsg(that.j_signup_email, that.j_signup_email_error_msg);
		}else {
			// do nothing
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function validateEmailExisting() {
	try{
		// server side check...
		if(!email_existing_validation_locked) {
			email_existing_validation_locked = true;
			var email = that.j_signup_email.val();
			if(email_existing_validating) {
				jqxhr_email.abort();
			}
			email_existing_validating = true;
			error_occured = false;
			
			jqxhr_email = jq.get(email_check_url, { 'email' : email }, validateEmailSuccess)
	    	.error(function() {
	    		error_occured = true; // block form submission
				var pos_top = that.j_signup_submit.offset().top;
	    		jq.notification({ "pos_top" : pos_top, "width" : 360, "message" : "Oops! An error occurred while processing your request." });
		    	
	    	})
	    	.complete(function() {
	   			// complete
				email_existing_validated = true;
	    		email_existing_validating = false;
	    	});
    	}else {
			email_existing_validated = true;
	    	email_existing_validating = false;
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function validateEmailSuccess(data) {
	try{
		if(data === "TRUE") {
			email_existing = true;
			that.j_signup_email.data({ 'errorMsg' : "Has already been taken" });
			showWarningMsg(that.j_signup_email, that.j_signup_email_error_msg);
		}else {
			email_existing = false;
			removeWarningMsg(that.j_signup_email, that.j_signup_email_error_msg);
		}
	}catch(err) {
		alert(err);
	}
	}

	function validateUsername() {
	try{
		if(!username_validation_locked) {
			username_validation_locked = true;
	    	if(!username_validation_ever_opened) {
	    		username_validation_ever_opened = true;
	    	}
			var username = that.j_signup_username.val();
			var usernameLength = jq.trim(username).length;
			
			// check required
			if(!usernameLength) {
				username_correct = false;
				that.j_signup_username.data({ 'errorMsg' : "Must not be empty" });
				showWarningMsg(that.j_signup_username, that.j_signup_username_error_msg);
				return false;
			}
			// check minimum length
			if(usernameLength < 3) {
				username_correct = false;
				that.j_signup_username.data({ 'errorMsg' : "At least 3 characters" });
				showWarningMsg(that.j_signup_username, that.j_signup_username_error_msg);
				return false;
			}
			// check maximum length
			if(usernameLength > 20) {
				username_correct = false;
				that.j_signup_username.data({ 'errorMsg' : "Less than 20 characters" });
				showWarningMsg(that.j_signup_username, that.j_signup_username_error_msg);
				return false;
			}
			// check regex
			if(username.search(/^[\u4e00-\u9fa5a-zA-Z0-9_]{3,20}$/) == -1) {
				username_correct = false;
				that.j_signup_username.data({ 'errorMsg' : "Not a valid username" });
				showWarningMsg(that.j_signup_username, that.j_signup_username_error_msg);
				return false;
			}
			
			username_correct = true;
			removeWarningMsg(that.j_signup_username, that.j_signup_username_error_msg);
    	}else {
    		// do nothing
    	}
	}catch(err) {
		alert(err);
	}
	}
	
	function validatePassword() {
	try{
		if(!password_validation_locked) {
			password_validation_locked = true;
	    	if(!password_validation_ever_opened) {
	    		password_validation_ever_opened = true;
	    	}
			var password = that.j_signup_password.val();
			var passLength = jq.trim(password).length;
			
			// check required
			if(!passLength) {
				password_correct = false;
				that.j_signup_password.data({ 'errorMsg' : "Must not be empty" });
				showWarningMsg(that.j_signup_password, that.j_signup_password_error_msg);
				return false;
			}
			// check minimum length
			if(passLength < 6) {
				password_correct = false;
				that.j_signup_password.data({ 'errorMsg' : "At least 6 characters" });
				showWarningMsg(that.j_signup_password, that.j_signup_password_error_msg);
				return false;
			}
			// check maximum length
			if(passLength > 20) {
				password_correct = false;
				that.j_signup_password.data({ 'errorMsg' : "Less than 20 characters" });
				showWarningMsg(that.j_signup_password, that.j_signup_password_error_msg);
				return false;
			}
			// check regex
			if(password.search(/^[a-zA-Z0-9_]{6,20}$/) == -1) {
				password_correct = false;
				that.j_signup_password.data({ 'errorMsg' : "Not a valid password" });
				showWarningMsg(that.j_signup_password, that.j_signup_password_error_msg);
				return false;
			}
			
			password_correct = true;
			removeWarningMsg(that.j_signup_password, that.j_signup_password_error_msg);
		}else {
			// do nothing
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function tosCheckboxChange() {
	try{
		tos_validation_locked = false;
		// only sumbit can open
    	if(tos_validation_ever_opened && !form_submiting) {
			validateTermsOfService();
    	}
	}catch(err) {
		alert(err);
	}
	}
	
	function validateTermsOfService() {
	try{
		if(!tos_validation_locked) {
			tos_validation_locked = true;
	    	if(!tos_validation_ever_opened) {
	    		tos_validation_ever_opened = true;
	    	}
			if(!that.j_signup_terms_of_service.is(':checked')) {
				if(!that.j_signup_tos_error_bubble.is(":visible")) {
					that.j_signup_tos_error_bubble.show();
				}
				tos_correct = false;
				return false;
			}
			tos_correct = true;
			if(that.j_signup_tos_error_bubble.is(":visible")) {
				that.j_signup_tos_error_bubble.hide();
			}
		}else {
			// do nothing
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function signupSumbitMousedown(e) {
	try{
		form_submiting = true;

		// reset
		email_existing_validated = false;
		
	}catch(err) {
		alert(err);
	}
	}
	
	function signupFormSubmit(e) {
	try{
    	e.preventDefault();

    	if(!email_validation_ever_opened) {
    		email_validation_ever_opened = true;
			email_validation_locked = false;
    	}
    	if(!username_validation_ever_opened) {
    		username_validation_ever_opened = true;
			username_validation_locked = false;
    	}
    	if(!password_validation_ever_opened) {
    		password_validation_ever_opened = true;
			password_validation_locked = false;
    	}
    	
		var signupForm = this;
		
		validateEmail();
		validateUsername();
		validatePassword();

		if(email_correct && username_correct && password_correct) {
    		if(!tos_validation_ever_opened) {
    			tos_validation_ever_opened = true;
				tos_validation_locked = false;
    		}
			validateTermsOfService();
			if(tos_correct) {
				that.j_signup_submit_status.css({ "display" : "inline-block" });
				
				validateEmailExisting();
				
				isEmailExistingValidated(signupForm);
			}else {
				form_submiting = false;
			}
		}else {
			form_submiting = false;
		}
		
	}catch(err) {
		alert(err);
	}
	}
	
	function isEmailExistingValidated(signupForm) {
	try{
		if(email_existing_validated) {
			form_submiting = false;
			that.j_signup_submit_status.css({ "display" : "none" });
			if(!email_existing && !error_occured) {
				signupForm.submit();
				return true;
			}
			return false;
		}else {
			setTimeout( function() {
				isEmailExistingValidated(signupForm);
			}, 300 );
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function showSignupTip(e) {
	try{
		var val = jq(this).data("explaination-text");
		var top_pos = jq(this).position().top;
		var left_pos = jq(this).position().left;
		if(!that.j_signup_tip_bubble.is(":visible")) {
			that.j_signup_tip.html(val);
			that.j_signup_tip_bubble.css({ top : top_pos+"px", left : left_pos+310+"px" }).fadeIn(200);
		}
		else {
			// clearQueue (clear hide)
			that.j_signup_tip_bubble.clearQueue().css({ top : top_pos+"px", left : left_pos+310+"px" });
			that.j_signup_tip.html(val);
		}
	}catch(err) {
		alert(err);
	}
	}

	function hideSignupTip(e) {
	try{
		// delay 10ms
		that.j_signup_tip_bubble.delay(10).fadeOut(200);
	}catch(err) {
		alert(err);
	}
	}
	
	function showWarningMsg(inputEl, errEl) {
	try{
		if (!inputEl.hasClass("error")){
			inputEl.addClass("error");
		}
		errEl.text(inputEl.data("errorMsg"));
		if(!errEl.is(":visible")) {
			errEl.show();
		}
	}catch(err) {
		alert(err);
	}
	}
	
	function removeWarningMsg(inputEl, errEl) {
	try{
		if(inputEl.hasClass("error")) {
			inputEl.removeClass("error");
		}
		if(errEl.is(":visible")) {
			errEl.hide();
		}
	}catch(err) {
		alert(err);
	}
	}
	
	__construct(param);
	
	that.j_signup_email.focus();
	
}catch(err) {
	alert(err);
}
}
